package edu.lnu.ums.service.impl;

import edu.lnu.ums.dal.exception.EntityManagerFactoryCreationException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;

import edu.lnu.ums.dal.IGenericDAO;
import edu.lnu.ums.dal.ISexDAO;
import edu.lnu.ums.dal.ICountryDAO;
import edu.lnu.ums.dal.IMonthsDAO;
import edu.lnu.ums.dal.ISubjectDAO;
import edu.lnu.ums.dal.IUMSObjectDAO;
import edu.lnu.ums.dal.IUMSTypeDAO;
import edu.lnu.ums.dal.IUserDAO;
import edu.lnu.ums.dal.factories.FactoriesCache;
import edu.lnu.ums.service.IDBInitService;
import edu.lnu.ums.entities.Faculty;
import edu.lnu.ums.entities.Human;
import edu.lnu.ums.entities.Student;
import edu.lnu.ums.entities.core.UMSObject;
import edu.lnu.ums.entities.dictionaries.Sex;
import edu.lnu.ums.entities.dictionaries.Country;
import edu.lnu.ums.entities.dictionaries.Months;
import edu.lnu.ums.entities.dictionaries.Subject;
import edu.lnu.ums.entities.dictionaries.UMSType;
import edu.lnu.ums.entities.security.User;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManager;
import org.hibernate.engine.SessionFactoryImplementor;

@Stateless
public class DBInitService implements IDBInitService {

    private static final String SQL_INITIALIZATION_PATH = "init.sql";
    private static final String DEFAULT_DS_PROPERTIES = "defaultDataSource.properties";
    private static final String DS_PATH_KEY = "defaultDSPath";
    private static final String DS_USER_KEY = "defaultDSUser";
    private static final String DS_PASSWORD_KEY = "defaultDSPassword";
     private static final String DS_PROVIDER_KEY = "defaultDSProvider";

    @EJB(mappedName = "UMS/SexDAO/local")
    private ISexDAO sexDao;
    @EJB(mappedName = "UMS/MonthsDAO/local")
    private IMonthsDAO monthsDao;
    @EJB(mappedName = "UMS/CountryDAO/local")
    private ICountryDAO countryDao;
    @EJB(mappedName = "UMS/UMSTypeDAO/local")
    private IUMSTypeDAO typeDao;
    @EJB(mappedName = "UMS/UMSObjectDAO/local")
    private IUMSObjectDAO umsObjectDAO;
    @EJB(mappedName="UMS/UserDAO/local")
    private IUserDAO userDAO;
    @EJB(mappedName="UMS/SubjectDAO/local")
    private ISubjectDAO subjectDao;

    private List<UMSType> typesList;

    @Override
    public void initializeAppAndDefaultDB() {
        initDefaultEntityManagerFactory();

        initializeDB(FactoriesCache.getInstance().getDefaultFactory());
    }
            

    @Override
    public void initializeDB(EntityManagerFactory factory) {
        if (isEmpty(factory, typeDao)) {
            initTypes(factory);
            initSexes(factory);
            initCountries(factory);
            initUMSObjects(factory);
            initTriggersAndRestrictions(factory);            
            initMonths(factory);
            initSubjects(factory);

            if (factory == FactoriesCache.getInstance().getDefaultFactory()) {
                initSecurityAccounts();
            }
        }
    }

    private void initTypes(EntityManagerFactory factory) {
        typesList = UMSType.getUMSTypes();
        
        for (UMSType type : typesList) {
            typeDao.persist(factory, type);
        }
    }

    private void initCountries(EntityManagerFactory factory) {
        Country ukr = new Country();
        ukr.setName("Україна");
        Country rus = new Country();
        rus.setName("Росія");
        countryDao.persist(factory, ukr);
        countryDao.persist(factory, rus);
    }

    private void initSexes(EntityManagerFactory factory) {
        Sex male = new Sex();
        male.setName("Чоловіча");
        Sex female = new Sex();
        female.setName("Жіноча");
        sexDao.persist(factory, male);
        sexDao.persist(factory, female);
    }

     private void initMonths(EntityManagerFactory factory) {
        Months 	january = new Months();
        january.setName("Січень");
        Months february = new Months();
        february.setName("Лютий");
        Months 	march = new Months();
        march.setName("Березень");
        Months april = new Months();
        april.setName("Квітень");
        Months may = new Months();
        may.setName("Травень");
        Months june = new Months();
        june.setName("Червень");
        Months july = new Months();
        july.setName("Липень");
        Months august  = new Months();
        august.setName("Серпень");
        Months september  = new Months();
        september.setName("Вересень");
        Months october  = new Months();
        october.setName("Жовтень");
        Months november  = new Months();
        november.setName("Листопад");
        Months december  = new Months();
        december.setName("Грудень");

        monthsDao.persist(factory, january);
        monthsDao.persist(factory, february);
        monthsDao.persist(factory, march);
        monthsDao.persist(factory, april);
        monthsDao.persist(factory, may);
        monthsDao.persist(factory, june);
        monthsDao.persist(factory, july);
        monthsDao.persist(factory, august);
        monthsDao.persist(factory, september);
        monthsDao.persist(factory, october);
        monthsDao.persist(factory, november);
        monthsDao.persist(factory, december);
    }

 private void initUMSObjects(EntityManagerFactory factory) {
        UMSObject root = UMSObject.createRoot();

        Human humanChild = new Human();
        Faculty facultyChild = new Faculty();

        List<UMSObject> children = new ArrayList<UMSObject>();
        children.add(humanChild);
        humanChild.setNodeName("HUMAN");
        humanChild.setSurname("His surname");
        facultyChild.setNodeName("FACULTY");
        facultyChild.setFullName("This faculty name");
        children.add(facultyChild);
        root.setChildren(children);
        humanChild.setParent(root);
        facultyChild.setParent(root);

        Student studentChild = new Student();
        studentChild.setCourse(20);
        studentChild.setSurname("Student surname");
        studentChild.setNodeName("Student node");
        studentChild.setParent(facultyChild);
        List<UMSObject> facultyChildren = new ArrayList<UMSObject>();
        facultyChildren.add(studentChild);
        facultyChild.setChildren(facultyChildren);

        humanChild.setChildren(Collections.EMPTY_LIST);
        studentChild.setChildren(Collections.EMPTY_LIST);

        umsObjectDAO.persist(factory, root);
    }

    private void initSubjects(EntityManagerFactory factory) {
        Subject math = new Subject("Математичний аналіз");
        Subject culture = new Subject("Культура");

        subjectDao.persist(factory, math);
        subjectDao.persist(factory, culture);
    }

    private boolean isEmpty(EntityManagerFactory factory, IGenericDAO<?> dao) {
        return dao.getAllOrdered(factory).size() == 0;
    }

    private void initTriggersAndRestrictions(EntityManagerFactory factory) {
        try {
             EntityManager em = factory.createEntityManager();
             HibernateEntityManager hib = (HibernateEntityManager)em;
             SessionFactory sessionFactory = hib.getSession().getSessionFactory();


            List<String> commands = getScriptStrings(SQL_INITIALIZATION_PATH);
            final Connection con = ((SessionFactoryImplementor) sessionFactory).getConnectionProvider().getConnection();


            for (String command : commands) {
                Statement statement = con.createStatement();
                try {
                    statement.execute(command);
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }

            con.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }

    }

    private List<String> getScriptStrings(String address) {
        List<String> retArr = new ArrayList<String>();

        InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(address);

        if (input == null) {
            throw new RuntimeException("File not found!");
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        String thisLine;
        try {
            thisLine = "";
            StringBuilder builder = new StringBuilder();
            while ((thisLine = reader.readLine()) != null) {
                if (!"".equals(thisLine)) {
                    builder.append(thisLine);
                    builder.append(" ");
                } else {
                    retArr.add(builder.toString());
                    builder = new StringBuilder();
                }

                thisLine = "";
            }

            return retArr;
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }

    }

    private void initSecurityAccounts() {
        User user = new User();
        user.setActive(Boolean.TRUE);
        user.setName("admin");
        user.setPassword("admin");
        user.setType("ADMIN");

        userDAO.addUser(user);
    }

    private void initDefaultEntityManagerFactory() {
        try {
            Properties props = new Properties();
            props.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(DEFAULT_DS_PROPERTIES));
            
            FactoriesCache.getInstance().addFactory(FactoriesCache.DEFAULT_FACTORY_NAME, 
                    props.getProperty(DS_PATH_KEY),
                    props.getProperty(DS_USER_KEY),
                    props.getProperty(DS_PASSWORD_KEY),
                    props.getProperty(DS_PROVIDER_KEY));

        } catch (EntityManagerFactoryCreationException ex) {
            ex.printStackTrace();
            throw new IllegalStateException("Default entity manager is not configured in a right way!");
        } catch (IOException ex) {
            ex.printStackTrace();
            throw new IllegalStateException("Default entity manager cannot load properties file!");
        }

    }
}
